#!/usr/bin/env python3
"""
MACD背离检测器
实现改进的背离检测算法，包括经典、隐性和柱状图背离
"""

import pandas as pd
import numpy as np
from scipy.signal import find_peaks
from typing import Dict, List, Tuple, Optional

class DivergenceDetector:
    """
    MACD背离检测器
    检测价格与MACD指标之间的背离现象
    """
    
    def __init__(self, lookback_period: int = 50, min_peak_distance: int = 5):
        """
        初始化背离检测器
        
        Args:
            lookback_period: 回看期间（天数）
            min_peak_distance: 峰值之间的最小距离
        """
        self.lookback_period = lookback_period
        self.min_peak_distance = min_peak_distance
        
        # 背离检测参数
        self.divergence_threshold = 0.02  # 背离阈值
        self.strength_threshold = 0.3     # 强度阈值
        self.confirmation_period = 3      # 确认期间
    
    def detect_divergences(self, price_series: pd.Series, macd_series: pd.Series, 
                          histogram_series: pd.Series) -> Dict:
        """
        检测所有类型的背离
        
        Args:
            price_series: 价格序列
            macd_series: MACD序列
            histogram_series: MACD柱状图序列
            
        Returns:
            包含所有背离检测结果的字典
        """
        try:
            # 确保数据长度足够
            if len(price_series) < self.lookback_period:
                return self._get_empty_result()
            
            # 取最近的数据
            recent_price = price_series.tail(self.lookback_period)
            recent_macd = macd_series.tail(self.lookback_period)
            recent_histogram = histogram_series.tail(self.lookback_period)
            
            # 检测看涨背离
            bullish_divergences = self._detect_bullish_divergences(
                recent_price, recent_macd, recent_histogram
            )
            
            # 检测看跌背离
            bearish_divergences = self._detect_bearish_divergences(
                recent_price, recent_macd, recent_histogram
            )
            
            # 评估整体背离强度
            divergence_strength = self._evaluate_divergence_strength(
                bullish_divergences, bearish_divergences
            )
            
            return {
                'bullish_divergences': bullish_divergences,
                'bearish_divergences': bearish_divergences,
                'divergence_strength': divergence_strength,
                'has_significant_divergence': (
                    bullish_divergences['any_detected'] or 
                    bearish_divergences['any_detected']
                ),
                'detection_timestamp': pd.Timestamp.now()
            }
            
        except Exception as e:
            print(f"背离检测过程中出现错误: {str(e)}")
            return self._get_empty_result()
    
    def _detect_bullish_divergences(self, price: pd.Series, macd: pd.Series, 
                                   histogram: pd.Series) -> Dict:
        """
        检测看涨背离
        
        Args:
            price: 价格序列
            macd: MACD序列
            histogram: 柱状图序列
            
        Returns:
            看涨背离检测结果
        """
        result = {
            'classic': {'detected': False, 'strength': 0.0, 'confidence': 0.0},
            'hidden': {'detected': False, 'strength': 0.0, 'confidence': 0.0},
            'histogram': {'detected': False, 'strength': 0.0, 'confidence': 0.0},
            'any_detected': False
        }
        
        try:
            # 1. 检测经典看涨背离
            classic_result = self._detect_classic_bullish_divergence(price, macd)
            result['classic'] = classic_result
            
            # 2. 检测隐性看涨背离
            hidden_result = self._detect_hidden_bullish_divergence(price, macd)
            result['hidden'] = hidden_result
            
            # 3. 检测柱状图看涨背离
            histogram_result = self._detect_histogram_bullish_divergence(price, histogram)
            result['histogram'] = histogram_result
            
            # 更新总体检测状态
            result['any_detected'] = (
                classic_result['detected'] or 
                hidden_result['detected'] or 
                histogram_result['detected']
            )
            
        except Exception as e:
            print(f"看涨背离检测错误: {str(e)}")
        
        return result
    
    def _detect_bearish_divergences(self, price: pd.Series, macd: pd.Series, 
                                   histogram: pd.Series) -> Dict:
        """
        检测看跌背离
        
        Args:
            price: 价格序列
            macd: MACD序列
            histogram: 柱状图序列
            
        Returns:
            看跌背离检测结果
        """
        result = {
            'classic': {'detected': False, 'strength': 0.0, 'confidence': 0.0},
            'hidden': {'detected': False, 'strength': 0.0, 'confidence': 0.0},
            'histogram': {'detected': False, 'strength': 0.0, 'confidence': 0.0},
            'any_detected': False
        }
        
        try:
            # 1. 检测经典看跌背离
            classic_result = self._detect_classic_bearish_divergence(price, macd)
            result['classic'] = classic_result
            
            # 2. 检测隐性看跌背离
            hidden_result = self._detect_hidden_bearish_divergence(price, macd)
            result['hidden'] = hidden_result
            
            # 3. 检测柱状图看跌背离
            histogram_result = self._detect_histogram_bearish_divergence(price, histogram)
            result['histogram'] = histogram_result
            
            # 更新总体检测状态
            result['any_detected'] = (
                classic_result['detected'] or 
                hidden_result['detected'] or 
                histogram_result['detected']
            )
            
        except Exception as e:
            print(f"看跌背离检测错误: {str(e)}")
        
        return result
    
    def _detect_classic_bullish_divergence(self, price: pd.Series, macd: pd.Series) -> Dict:
        """
        检测经典看涨背离：价格创新低，MACD不创新低
        """
        try:
            # 找到价格和MACD的低点
            price_lows = self._find_lows(price.values)
            macd_lows = self._find_lows(macd.values)
            
            if len(price_lows) < 2 or len(macd_lows) < 2:
                return {'detected': False, 'strength': 0.0, 'confidence': 0.0}
            
            # 检查最近的两个低点
            recent_price_lows = price_lows[-2:]
            recent_macd_lows = macd_lows[-2:]
            
            # 价格创新低
            price_lower = price.iloc[recent_price_lows[1]] < price.iloc[recent_price_lows[0]]
            
            # MACD不创新低（或创更高低点）
            macd_higher = macd.iloc[recent_macd_lows[1]] > macd.iloc[recent_macd_lows[0]]
            
            if price_lower and macd_higher:
                # 计算背离强度
                price_change = (price.iloc[recent_price_lows[0]] - price.iloc[recent_price_lows[1]]) / price.iloc[recent_price_lows[0]]
                macd_change = (macd.iloc[recent_macd_lows[1]] - macd.iloc[recent_macd_lows[0]]) / abs(macd.iloc[recent_macd_lows[0]])
                
                strength = min(abs(price_change) + abs(macd_change), 1.0)
                confidence = self._calculate_divergence_confidence(
                    recent_price_lows, recent_macd_lows, len(price)
                )
                
                return {
                    'detected': True,
                    'strength': strength,
                    'confidence': confidence
                }
            
        except Exception as e:
            print(f"经典看涨背离检测错误: {str(e)}")
        
        return {'detected': False, 'strength': 0.0, 'confidence': 0.0}
    
    def _detect_classic_bearish_divergence(self, price: pd.Series, macd: pd.Series) -> Dict:
        """
        检测经典看跌背离：价格创新高，MACD不创新高
        """
        try:
            # 找到价格和MACD的高点
            price_highs = self._find_peaks(price.values)
            macd_highs = self._find_peaks(macd.values)
            
            if len(price_highs) < 2 or len(macd_highs) < 2:
                return {'detected': False, 'strength': 0.0, 'confidence': 0.0}
            
            # 检查最近的两个高点
            recent_price_highs = price_highs[-2:]
            recent_macd_highs = macd_highs[-2:]
            
            # 价格创新高
            price_higher = price.iloc[recent_price_highs[1]] > price.iloc[recent_price_highs[0]]
            
            # MACD不创新高（或创更低高点）
            macd_lower = macd.iloc[recent_macd_highs[1]] < macd.iloc[recent_macd_highs[0]]
            
            if price_higher and macd_lower:
                # 计算背离强度
                price_change = (price.iloc[recent_price_highs[1]] - price.iloc[recent_price_highs[0]]) / price.iloc[recent_price_highs[0]]
                macd_change = (macd.iloc[recent_macd_highs[0]] - macd.iloc[recent_macd_highs[1]]) / abs(macd.iloc[recent_macd_highs[0]])
                
                strength = min(abs(price_change) + abs(macd_change), 1.0)
                confidence = self._calculate_divergence_confidence(
                    recent_price_highs, recent_macd_highs, len(price)
                )
                
                return {
                    'detected': True,
                    'strength': strength,
                    'confidence': confidence
                }
            
        except Exception as e:
            print(f"经典看跌背离检测错误: {str(e)}")
        
        return {'detected': False, 'strength': 0.0, 'confidence': 0.0}
    
    def _detect_hidden_bullish_divergence(self, price: pd.Series, macd: pd.Series) -> Dict:
        """
        检测隐性看涨背离：价格创更高低点，MACD创更低低点
        """
        try:
            # 找到价格和MACD的低点
            price_lows = self._find_lows(price.values)
            macd_lows = self._find_lows(macd.values)
            
            if len(price_lows) < 2 or len(macd_lows) < 2:
                return {'detected': False, 'strength': 0.0, 'confidence': 0.0}
            
            # 检查最近的两个低点
            recent_price_lows = price_lows[-2:]
            recent_macd_lows = macd_lows[-2:]
            
            # 价格创更高低点
            price_higher_low = price.iloc[recent_price_lows[1]] > price.iloc[recent_price_lows[0]]
            
            # MACD创更低低点
            macd_lower_low = macd.iloc[recent_macd_lows[1]] < macd.iloc[recent_macd_lows[0]]
            
            if price_higher_low and macd_lower_low:
                # 计算背离强度
                price_change = (price.iloc[recent_price_lows[1]] - price.iloc[recent_price_lows[0]]) / price.iloc[recent_price_lows[0]]
                macd_change = (macd.iloc[recent_macd_lows[0]] - macd.iloc[recent_macd_lows[1]]) / abs(macd.iloc[recent_macd_lows[0]])
                
                strength = min(abs(price_change) + abs(macd_change), 1.0) * 0.8  # 隐性背离强度稍低
                confidence = self._calculate_divergence_confidence(
                    recent_price_lows, recent_macd_lows, len(price)
                ) * 0.9  # 隐性背离置信度稍低
                
                return {
                    'detected': True,
                    'strength': strength,
                    'confidence': confidence
                }
            
        except Exception as e:
            print(f"隐性看涨背离检测错误: {str(e)}")
        
        return {'detected': False, 'strength': 0.0, 'confidence': 0.0}
    
    def _detect_hidden_bearish_divergence(self, price: pd.Series, macd: pd.Series) -> Dict:
        """
        检测隐性看跌背离：价格创更低高点，MACD创更高高点
        """
        try:
            # 找到价格和MACD的高点
            price_highs = self._find_peaks(price.values)
            macd_highs = self._find_peaks(macd.values)
            
            if len(price_highs) < 2 or len(macd_highs) < 2:
                return {'detected': False, 'strength': 0.0, 'confidence': 0.0}
            
            # 检查最近的两个高点
            recent_price_highs = price_highs[-2:]
            recent_macd_highs = macd_highs[-2:]
            
            # 价格创更低高点
            price_lower_high = price.iloc[recent_price_highs[1]] < price.iloc[recent_price_highs[0]]
            
            # MACD创更高高点
            macd_higher_high = macd.iloc[recent_macd_highs[1]] > macd.iloc[recent_macd_highs[0]]
            
            if price_lower_high and macd_higher_high:
                # 计算背离强度
                price_change = (price.iloc[recent_price_highs[0]] - price.iloc[recent_price_highs[1]]) / price.iloc[recent_price_highs[0]]
                macd_change = (macd.iloc[recent_macd_highs[1]] - macd.iloc[recent_macd_highs[0]]) / abs(macd.iloc[recent_macd_highs[0]])
                
                strength = min(abs(price_change) + abs(macd_change), 1.0) * 0.8  # 隐性背离强度稍低
                confidence = self._calculate_divergence_confidence(
                    recent_price_highs, recent_macd_highs, len(price)
                ) * 0.9  # 隐性背离置信度稍低
                
                return {
                    'detected': True,
                    'strength': strength,
                    'confidence': confidence
                }
            
        except Exception as e:
            print(f"隐性看跌背离检测错误: {str(e)}")
        
        return {'detected': False, 'strength': 0.0, 'confidence': 0.0}
    
    def _detect_histogram_bullish_divergence(self, price: pd.Series, histogram: pd.Series) -> Dict:
        """
        检测柱状图看涨背离
        """
        try:
            # 找到价格低点和柱状图低点
            price_lows = self._find_lows(price.values)
            histogram_lows = self._find_lows(histogram.values)
            
            if len(price_lows) < 2 or len(histogram_lows) < 2:
                return {'detected': False, 'strength': 0.0, 'confidence': 0.0}
            
            # 检查最近的两个低点
            recent_price_lows = price_lows[-2:]
            recent_histogram_lows = histogram_lows[-2:]
            
            # 价格创新低
            price_lower = price.iloc[recent_price_lows[1]] < price.iloc[recent_price_lows[0]]
            
            # 柱状图不创新低
            histogram_higher = histogram.iloc[recent_histogram_lows[1]] > histogram.iloc[recent_histogram_lows[0]]
            
            if price_lower and histogram_higher:
                # 计算背离强度
                price_change = (price.iloc[recent_price_lows[0]] - price.iloc[recent_price_lows[1]]) / price.iloc[recent_price_lows[0]]
                histogram_change = abs(histogram.iloc[recent_histogram_lows[1]] - histogram.iloc[recent_histogram_lows[0]])
                
                strength = min(abs(price_change) + histogram_change * 0.5, 1.0)
                confidence = self._calculate_divergence_confidence(
                    recent_price_lows, recent_histogram_lows, len(price)
                ) * 0.85  # 柱状图背离置信度稍低
                
                return {
                    'detected': True,
                    'strength': strength,
                    'confidence': confidence
                }
            
        except Exception as e:
            print(f"柱状图看涨背离检测错误: {str(e)}")
        
        return {'detected': False, 'strength': 0.0, 'confidence': 0.0}
    
    def _detect_histogram_bearish_divergence(self, price: pd.Series, histogram: pd.Series) -> Dict:
        """
        检测柱状图看跌背离
        """
        try:
            # 找到价格高点和柱状图高点
            price_highs = self._find_peaks(price.values)
            histogram_highs = self._find_peaks(histogram.values)
            
            if len(price_highs) < 2 or len(histogram_highs) < 2:
                return {'detected': False, 'strength': 0.0, 'confidence': 0.0}
            
            # 检查最近的两个高点
            recent_price_highs = price_highs[-2:]
            recent_histogram_highs = histogram_highs[-2:]
            
            # 价格创新高
            price_higher = price.iloc[recent_price_highs[1]] > price.iloc[recent_price_highs[0]]
            
            # 柱状图不创新高
            histogram_lower = histogram.iloc[recent_histogram_highs[1]] < histogram.iloc[recent_histogram_highs[0]]
            
            if price_higher and histogram_lower:
                # 计算背离强度
                price_change = (price.iloc[recent_price_highs[1]] - price.iloc[recent_price_highs[0]]) / price.iloc[recent_price_highs[0]]
                histogram_change = abs(histogram.iloc[recent_histogram_highs[0]] - histogram.iloc[recent_histogram_highs[1]])
                
                strength = min(abs(price_change) + histogram_change * 0.5, 1.0)
                confidence = self._calculate_divergence_confidence(
                    recent_price_highs, recent_histogram_highs, len(price)
                ) * 0.85  # 柱状图背离置信度稍低
                
                return {
                    'detected': True,
                    'strength': strength,
                    'confidence': confidence
                }
            
        except Exception as e:
            print(f"柱状图看跌背离检测错误: {str(e)}")
        
        return {'detected': False, 'strength': 0.0, 'confidence': 0.0}
    
    def _find_peaks(self, data: np.ndarray) -> List[int]:
        """
        找到数据中的峰值点
        """
        try:
            peaks, _ = find_peaks(data, distance=self.min_peak_distance)
            return peaks.tolist()
        except:
            return []
    
    def _find_lows(self, data: np.ndarray) -> List[int]:
        """
        找到数据中的低点
        """
        try:
            # 通过反转数据来找低点
            inverted_data = -data
            peaks, _ = find_peaks(inverted_data, distance=self.min_peak_distance)
            return peaks.tolist()
        except:
            return []
    
    def _calculate_divergence_confidence(self, price_points: List[int], 
                                       indicator_points: List[int], 
                                       total_length: int) -> float:
        """
        计算背离的置信度
        """
        try:
            # 基于时间间隔的置信度
            time_distance = abs(price_points[1] - price_points[0])
            time_confidence = min(time_distance / 10.0, 1.0)  # 时间间隔越大，置信度越高
            
            # 基于点位接近程度的置信度
            position_diff = abs(price_points[1] - indicator_points[1])
            position_confidence = max(0.5, 1.0 - position_diff / 20.0)
            
            # 基于数据完整性的置信度
            data_confidence = min(total_length / self.lookback_period, 1.0)
            
            # 综合置信度
            overall_confidence = (time_confidence * 0.4 + 
                                position_confidence * 0.4 + 
                                data_confidence * 0.2)
            
            return max(0.1, min(1.0, overall_confidence))
            
        except:
            return 0.5  # 默认置信度
    
    def _evaluate_divergence_strength(self, bullish_div: Dict, bearish_div: Dict) -> Dict:
        """
        评估整体背离强度
        """
        try:
            # 计算看涨背离总强度
            bullish_strength = 0.0
            if bullish_div['any_detected']:
                bullish_strength = max(
                    bullish_div['classic']['strength'],
                    bullish_div['hidden']['strength'],
                    bullish_div['histogram']['strength']
                )
            
            # 计算看跌背离总强度
            bearish_strength = 0.0
            if bearish_div['any_detected']:
                bearish_strength = max(
                    bearish_div['classic']['strength'],
                    bearish_div['hidden']['strength'],
                    bearish_div['histogram']['strength']
                )
            
            # 确定主导方向
            if bullish_strength > bearish_strength:
                direction = 'bullish'
                overall_strength = bullish_strength
            elif bearish_strength > bullish_strength:
                direction = 'bearish'
                overall_strength = bearish_strength
            else:
                direction = 'neutral'
                overall_strength = max(bullish_strength, bearish_strength)
            
            # 评估可靠性
            if overall_strength >= 0.7:
                reliability = 'high'
            elif overall_strength >= 0.4:
                reliability = 'medium'
            else:
                reliability = 'low'
            
            # 计算置信度
            confidence = overall_strength
            if bullish_div['any_detected'] and bearish_div['any_detected']:
                confidence *= 0.8  # 如果同时存在两种背离，降低置信度
            
            return {
                'direction': direction,
                'overall_strength': overall_strength,
                'reliability': reliability,
                'confidence': confidence,
                'bullish_strength': bullish_strength,
                'bearish_strength': bearish_strength
            }
            
        except Exception as e:
            print(f"背离强度评估错误: {str(e)}")
            return {
                'direction': 'neutral',
                'overall_strength': 0.0,
                'reliability': 'low',
                'confidence': 0.0,
                'bullish_strength': 0.0,
                'bearish_strength': 0.0
            }
    
    def _get_empty_result(self) -> Dict:
        """
        获取空的检测结果
        """
        return {
            'bullish_divergences': {
                'classic': {'detected': False, 'strength': 0.0, 'confidence': 0.0},
                'hidden': {'detected': False, 'strength': 0.0, 'confidence': 0.0},
                'histogram': {'detected': False, 'strength': 0.0, 'confidence': 0.0},
                'any_detected': False
            },
            'bearish_divergences': {
                'classic': {'detected': False, 'strength': 0.0, 'confidence': 0.0},
                'hidden': {'detected': False, 'strength': 0.0, 'confidence': 0.0},
                'histogram': {'detected': False, 'strength': 0.0, 'confidence': 0.0},
                'any_detected': False
            },
            'divergence_strength': {
                'direction': 'neutral',
                'overall_strength': 0.0,
                'reliability': 'low',
                'confidence': 0.0,
                'bullish_strength': 0.0,
                'bearish_strength': 0.0
            },
            'has_significant_divergence': False,
            'detection_timestamp': pd.Timestamp.now()
        }